/***************************************************************************
 * Copyright (C) 2010
 * by Dimok
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * for WiiXplorer 2010
 ***************************************************************************/
#include <ogcsys.h>
#include <stdio.h>
#include <string.h>
#include "http.h"

#include "Download.h"

using namespace std;


Download::Download() {
	Download::console = &Console::GetConsole();
	console->Print("Network init...");
	
    int result = if_config(IP, NULL, NULL, true);
	if(result < 0)
		console->Print("Network failed.");
}

Download::~Download() {
	
}

bool Download::Update() {
	unsigned char *buffer;
	size_t size = 0;
	
	int ret = DownloadFileToMem("http://the-platformer.googlecode.com/svn/trunk/update.txt", &buffer, &size);
	if(ret < 0) {
		console->Print("Failed to get rev");
		return false;
	}
	
	char tmp[6] = {0};
	strncpy(tmp, (const char *)buffer, size);
	
	int currentRev = atoi(tmp);
	if(currentRev > REV) {
		char path[256] = "http://the-platformer.googlecode.com/svn/trunk/";
		char rev[32] = {0};
		sprintf(rev, "r%d.dol", currentRev);
		strcat(path, rev);
		DownloadFileToPath(path, "/apps/platform/boot.dol");
	} else {
		console->Print("No Updates.");
	}
	
	return true;
}

/****************************************************************************
 * Download a file from a given url
 ****************************************************************************/
int Download::DownloadFileToMem(const char *url, u8 **inbuffer, u32 *size) {
    int domainlength = this->ExtractDomain(url);
	if(domainlength < 0) {
		console->Print("Domainlength is 0");
		return -1;
	}

    char domain[domainlength + 1];
    strncpy(domain, url + strlen("http://"), domainlength);
    domain[domainlength] = '\0';

    int connection = GetConnection(domain);

    if(connection < 0) {
		console->Print("Could not connect to the server.");
        return -4;
    }

    char header[1024];
    char * ptr = header;
    ptr += sprintf(ptr, "GET %s HTTP/1.1\r\n", path);
    ptr += sprintf(ptr, "Host: %s\r\n", domain);
    ptr += sprintf(ptr, "Referer: %s\r\n", domain);
    ptr += sprintf(ptr, "User-Agent: Unknown\r\n");
    ptr += sprintf(ptr, "Pragma: no-cache\r\n");
    ptr += sprintf(ptr, "Cache-Control: no-cache\r\n");
    ptr += sprintf(ptr, "Connection: close\r\n\r\n");

    char filename[255];
	char tmp[80];
    memset(filename, 0, sizeof(filename));

    int filesize = network_request(connection, header, filename);
	sprintf(tmp, "Filesize is %d KB", filesize/1024);
	console->Print(tmp);

    if(filesize <= 0) {
        net_close(connection);
		console->Print("Filesize is 0 Byte.");
        return -5;
    }

    int blocksize = 5*1024;

    u8 *buffer = (u8 *)malloc(filesize);
    if(!buffer) {
        net_close(connection);
		console->Print("Not enough memory.");
        return -6;
    }

    int done = 0;
	
	console->Print(" ");
    while(done < filesize) {
        if(blocksize > filesize - done)
            blocksize = filesize - done;


        s32 read = network_read(connection, buffer+done, blocksize);

        if(read < 0) {
                done = -8;
				console->Print("Transfer failed.");
                break;
        } else if(!read)
            break;

        done += read;
		
		// Show status
		console->Pop_back();
		sprintf(tmp, "Downloading: %d/%d KB", done/1024, filesize/1024);
		console->Print(string(tmp));
    }
    net_close(connection);

    if(done < 0) {
        free(buffer);
		
        return done;
    }
    *inbuffer = buffer;
    *size = filesize;
	
    return done;
}

/****************************************************************************
 * Download a file from a given url to a given path
 ****************************************************************************/
int Download::DownloadFileToPath(const char *orig_url, const char *dest) {
    if(!orig_url || !dest) {
		console->Print("No URL or Path specified.");
        return -1;
    }
	
	u32 filesize = 0;
	u8 *buffer;

    int done = this->DownloadFileToMem(orig_url, &buffer, &filesize);
	if(done <= 0) {
		console->Print("Return < 0");
		return -2;
	}
	
	FILE *file = fopen(dest, "wb");
    if(!file) {
		console->Print("Cannot write to destination.");
        return -3;
    }
	
	done = fwrite(buffer, filesize, 1, file);
	if(done <= 0) {
		console->Print("Writing file to SD failed.");
		return -4;
	}
	
	console->Print("Update successfully.");
	
    return done;
}

size_t Download::ExtractDomain(const char *url) {
	if(strncasecmp(url, "http://", strlen("http://")) != 0) {
		console->Print("Not a valid URL.");
        return -1;
    }
    
	path = strchr(url + strlen("http://"), '/');

    if(!path) {
		console->Print("Not a valid URL path.");
        return -2;
    }

    int domainlength = path - url - strlen("http://");

    if(domainlength == 0) {
		console->Print("Not a valid domain.");
        return -3;
    }
	
	return domainlength;
}

char *Download::GetIP() {
	return IP;
}
